<template>
  <el-form
    ref="ruleFormRef"
    :model="newRuleForm"
    v-bind="$attrs"
    :label-width="$attrs.labelWidth || 110"
  >
    <el-row>
      <el-col v-for="(item, index) in newFormData" :key="'form_' + index" :span="item.span || span">
        <el-form-item
          v-if="isTrue(item.display)"
          :label="item.label"
          :prop="item.prop"
          :rules="[
            {
              required: isTrue(item.required),
              message: item.message || '请输入'
            },
            setPattern(item)
          ]"
        >
          <!--自定义标签-->
          <template #label>
            <div @click.prevent="">
              {{ item.label }}
            </div>
          </template>

          <!--自定义插槽：slot-->
          <div v-if="item.type == 'slot'">
            <slot :name="item.name" :ruleForm="newRuleForm"></slot>
          </div>

          <!--纯文本:text-->
          <div v-if="item.type == 'text'">
            {{ newRuleForm[item.prop] }}
          </div>

          <!--纯数字-->
          <el-input
            v-if="item.type === 'number'"
            v-model.trim="newRuleForm[item.prop]"
            :minlength="item.minlength"
            :maxlength="item.maxlength"
            :min="item.min"
            :max="item.max"
            :show-word-limit="item.limit"
            :disabled="item.disabled"
            :readonly="item.readonly"
            :placeholder="item.placeholder || '请输入'"
            type="text"
          >
            <!--尾部自定义内容-->
            <template v-if="item.suffix" #suffix>
              <span style="padding-left: 2px">{{ item.suffix }}</span>
            </template>
            <!--头部自定义内容-->
            <template v-if="item.prefix" #prefix>
              <span style="padding-right: 2px">{{ item.prefix }}</span>
            </template>
          </el-input>

          <!--普通文本框:input-->
          <el-input
            v-if="item.type === 'input'"
            v-model.trim="newRuleForm[item.prop]"
            :minlength="item.minlength"
            :maxlength="item.maxlength"
            :show-word-limit="item.limit"
            :disabled="item.disabled"
            :readonly="item.readonly"
            :placeholder="item.placeholder || item.message"
            :type="item.password ? 'password' : 'text'"
            :prefix-icon="item.search ? Search : ''"
            @input="(v) => setInputValue(newRuleForm, item, v)"
          >
            <template v-if="item.prepend" #prepend>
              <el-select
                v-if="item.prependProp"
                v-model="newRuleForm[item.prependProp]"
                placeholder="请选择"
                style="width: 80px"
              >
                <el-option
                  v-for="_item in item.prependList"
                  :key="_item.value"
                  :label="_item.label"
                  :value="_item.value"
                />
              </el-select>
              <span v-if="item.prependText"> {{ item.prependText }}</span>
            </template>
            <template v-if="item.appendText" #append>{{ item.appendText }}</template>
            <!--尾部搜索图标-->
            <template v-if="item.appendSearch || item.suffix" #suffix>
              <span v-if="item.suffix" style="padding-left: 2px">{{ item.suffix }}</span>
              <el-icon
                v-else
                @click.stop="changeVal(newRuleForm[item.prop], item.prop)"
                style="cursor: pointer"
                ><Search
              /></el-icon>
            </template>
            <!--头部自定义内容-->
            <template v-if="item.prefix" #prefix>
              <span style="padding-right: 2px">{{ item.prefix }}</span>
            </template>
          </el-input>

          <!--文本区域框:textarea-->
          <el-input
            v-if="item.type === 'textarea'"
            v-model.trim="newRuleForm[item.prop]"
            type="textarea"
            :minlength="item.minlength"
            :maxlength="item.maxlength"
            :show-word-limit="item.limit"
            :resize="item.resize || 'both'"
            :disabled="item.disabled"
            :readonly="item.readonly"
            :placeholder="item.placeholder || item.message"
          />

          <!--swtich表单-->
          <el-switch
            v-if="item.type === 'switch'"
            v-model="newRuleForm[item.prop]"
            inline-prompt
            :disabled="item.disabled"
            :active-text="item.activeText"
            :inactive-text="item.inactiveText"
            :active-value="item.activeValue"
            :inactive-value="item.inactiveValue"
            @change="changeVal($event, item.prop)"
          />

          <!--单选框：radio-->
          <el-radio-group
            v-if="item.type === 'radio'"
            v-model="newRuleForm[item.prop]"
            :disabled="item.disabled"
            @change="changeVal($event, item.prop)"
          >
            <el-radio v-for="(_item, _index) in item.list" :key="_index" :label="_item.value">
              {{ _item.label }}
              <el-tooltip v-if="_item.tips" :content="_item.tips" placement="bottom" effect="light">
                <el-icon>
                  <Warning />
                </el-icon>
              </el-tooltip>
            </el-radio>
          </el-radio-group>
          <!--多选框：checkbox-->
          <el-checkbox-group
            v-if="item.type === 'checkbox'"
            v-model="newRuleForm[item.prop]"
            :disabled="item.disabled"
            @change="($event) => changeVal($event, item.prop)"
          >
            <el-checkbox v-for="(_item, _index) in item.list" :key="_index" :label="_item.value">
              {{ _item.label }}
            </el-checkbox>
          </el-checkbox-group>

          <!--下拉选择：select-->
          <el-select
            v-if="item.type === 'select'"
            v-model="newRuleForm[item.prop]"
            :multiple="item.multiple"
            :collapse-tags="item.collapseTags"
            :disabled="item.disabled"
            :filterable="isTrue(item.filterable)"
            clearable
            :multiple-limit="item.multipleLimit"
            :placeholder="item.placeholder || '请选择'"
            @change="($event) => changeVal($event, item.prop)"
            @input="($event) => changeVal($event, item.prop)"
          >
            <template v-if="item.group">
              <el-option-group v-for="group in item.list" :key="group.label" :label="group.label">
                <el-option
                  v-for="_item in group.options"
                  :key="_item.value"
                  :label="_item.label"
                  :value="_item.value"
                />
              </el-option-group>
            </template>
            <template v-else>
              <el-option
                v-for="(_item, _index) in item.list"
                :key="_index"
                :label="_item.label"
                :value="_item.value"
              >
              </el-option>
            </template>
          </el-select>

          <!--级联选择器：cascader-->
          <el-cascader
            v-if="item.type === 'cascader'"
            :props="{ multiple: false, emitPath: true }"
            :show-all-levels="false"
            :options="item.list"
            v-model="newRuleForm[item.prop]"
            :placeholder="item.placeholder"
            :disabled="item.disabled"
          />

          <!--日期: date-->
          <el-date-picker
            v-if="item.type === 'date' || item.type === 'daterange'"
            v-model="newRuleForm[item.prop]"
            :type="item.type"
            :placeholder="item.placeholder || '请选择日期'"
            :disabled="item.disabled"
            :startPlaceholder="item.startPlaceholder || '开始日期'"
            :endPlaceholder="item.endPlaceholder || '结束日期'"
            :rangeSeparator="item.rangeSeparator || '至'"
            format="YYYY-MM-DD"
            value-format="YYYY-MM-DD"
            :disabled-date="item.disabledDate"
          />

          <!--日期时间: dateTime-->
          <el-date-picker
            v-if="item.type === 'datetime' || item.type === 'datetimerange'"
            v-model="newRuleForm[item.prop]"
            :type="item.type"
            :placeholder="item.placeholder || '请选择日期'"
            :disabled="item.disabled"
            :startPlaceholder="item.startPlaceholder || '开始日期'"
            :endPlaceholder="item.endPlaceholder || '结束日期'"
            :rangeSeparator="item.rangeSeparator || '至'"
            format="YYYY-MM-DD HH:mm:ss"
            value-format="YYYY-MM-DD HH:mm:ss"
            :disabled-date="item.disabledDate"
          />
        </el-form-item>
      </el-col>
    </el-row>
  </el-form>
</template>

<script setup>
import { Warning, Search } from '@element-plus/icons-vue'
import { ref, watch, toRaw, nextTick } from 'vue'
import { deepClone, isTrue } from '../../lib'
const emits = defineEmits(['submit', 'changeVal', 'update:modelValue', 'update:ruleForm'])
const props = defineProps({
  // 表单配置项
  modelValue: {
    type: Array,
    default: () => {
      return []
    }
  },
  // 表单填充值
  ruleForm: {
    type: Object,
    default: () => {
      return {}
    }
  },
  // 表单每列栅格数
  span: {
    type: [String, Number],
    default: 24
  }
})

const ruleFormRef = ref(null)
const newFormData = ref([])
const newRuleForm = ref([])

/**
 * 同步表单对象到ruleForm
 */
watch(
  () => newRuleForm.value,
  (val) => {
    emits('update:ruleForm', val)
  }
)

/**
 * 初始化或重置表单对象，通过表单配置项生成表单对象,使用默认的ruleForm进行覆盖
 * 表单对象为：display显示并且有prop属性
 */
const initForm = (ruleForm = {}) => {
  newFormData.value = deepClone(props.modelValue)
  const keys = {}
  props.modelValue
    .filter((item) => isTrue(item.display) && item.prop)
    .forEach((item) => {
      keys[item.prop] = item.value
    })
  nextTick(() => {
    newRuleForm.value = { ...keys, ...ruleForm }
    ruleFormRef.value.resetFields()
  })
}

/**
 * 监听表单配置项, 渲染表单并生成表单对象
 */
watch(
  () => props.modelValue,
  (val) => {
    if (val.length > 0) {
      initForm(props.ruleForm)
    }
  },
  {
    immediate: true
  }
)

/**
 * 修改表单对象属性值
 * @param { string } obj 属性对象
 */
const modifyFormValue = (obj) => {
  newRuleForm.value = {
    ...newRuleForm.value,
    ...obj
  }
}

/**
 * 修改表单配置项
 * @param { string } obj 属性对象
 */
const modifyFormConfig = (obj) => {
  for (let key in obj) {
    const index = newFormData.value.findIndex((item) => item.prop === key)
    if (index > -1) {
      for (let prop in obj[key]) {
        newFormData.value[index][prop] = obj[key][prop]
        if (prop === 'value') newRuleForm.value[key] = obj[key][prop]
      }
    }
  }
}

/**
 * 提交表单验证
 */
const submitForm = async () => {
  try {
    const valid = await ruleFormRef.value.validate()
    if (valid) {
      const formItem = toRaw(newRuleForm.value)
      emits('submit', formItem)
      return new Promise((resolve) => {
        resolve(formItem)
      })
    }
  } catch (err) {
    return new Promise((resolve) => {
      resolve(null)
    })
  }
}

/**
 * 表单正则匹配配置项，表单默认配置不能输入空格
 * @param { Object } item 表单配置项
 */
const setPattern = (item) => {
  const params = {
    pattern: item.pattern || /^/g
  }
  if (item.validator) {
    params.validator = validator
  } else {
    params.message = item.patternMessage || item.message || item.placeholder
  }
  return params
}

/**
 * 用户输入时动态匹配正则表达式
 * @param { Object } scope 配置项
 * @param { Object } item 表单对象
 * @param { string } v 当前值
 */
const setInputValue = (ruleForm, item, v) => {
  return v
  ruleForm[item.prop] = v.replace(/[^\d]/g, '')
  // 浮点数
  const str = []
  for (let i = 0; i < item.floatNum; i++) {
    str.push('\\d')
  }
  const num = str.join('')
  var reg1 = '/^(\\-)*(\\d+)\\.(' + num + ').*$/'
  ruleForm[item.prop] = v
    .replace(/[^\d\\.]/g, '')
    .replace(/\.{2,}/g, '.')
    .replace('.', '$#$')
    .replace(/\./g, '')
    .replace('$#$', '.')
    .replace(eval(reg1), '$1$2.$3')
}

/**
 * 重置表单
 */
const resetForm = () => {
  initForm()
}

/**
 * 重置表单
 */
const validateField = (prop) => {
  ruleFormRef.value.validateField(prop)
}

/**
 * 触发表单项变化时事件
 */
const changeVal = (ruleForm, prop) => {
  emits('changeVal', ruleForm, prop)
}

/**
 * 自定义校验
 * @param {*} rules
 * @param {*} val
 * @param {*} callback
 */
const validator = (rules, val, callback) => {
  emits('changeVal', val, rules.field, callback)
}

/**
 * 清除某个表单验证信息
 * @param {*} prop
 */
const clearValidate = (prop) => {
  ruleFormRef.value.clearValidate(prop)
}

defineExpose({
  submitForm,
  initForm,
  resetForm,
  modifyFormValue,
  modifyFormConfig,
  clearValidate,
  validateField
})
</script>

<style scoped lang="scss"></style>
